/*
 *         (C) COPYRIGHT NBI Limited
 *              ALL RIGHT RESERVED
 *
 * File        : fastflow/core/common.h
 * Authors     : dzhang
 * Create Time : 2021/09/06 14:41:24
 * Description :
 *
 */

#ifndef __FASTFLOW_CORE_COMMON_H___
#define __FASTFLOW_CORE_COMMON_H___

#include <algorithm>
#include <chrono>
#include <functional>
#include <memory>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include "fastflow/core/macros.h"
#include "fastflow/core/code_location.h"
#include "fastflow/core/exceptions.h"
#include "fastflow/core/status.h"
#include "fastflow/core/string_utils.h"
#include "fastflow/framework/error_code.h"

namespace fastflow {

#ifdef _WIN32
#define FASTFLOW_UNUSED_PARAMETER(x) (x)
#else
#define FASTFLOW_UNUSED_PARAMETER(x) (void)(x)
#endif

#ifndef FASTFLOW_HAVE_ATTRIBUTE
#ifdef __has_attribute
#define FASTFLOW_HAVE_ATTRIBUTE(x) __has_attribute(x)
#else
#define FASTFLOW_HAVE_ATTRIBUTE(x) 0
#endif
#endif

//TODO: encode error code in the message?
#define FASTFLOW_THROW_ON_ERROR(expr)                                                \
  do {                                                                                  \
    ONNXStatusPtr onnx_status = (expr);                                                 \
    if (onnx_status != nullptr) {                                                       \
      std::string onnx_runtime_error_message = FASTFLOWGetErrorMessage(onnx_status); \
      ReleaseONNXStatus(onnx_status);                                                   \
      throw std::runtime_error(onnx_runtime_error_message);                             \
    }                                                                                   \
  } while (0);


// For propagating errors when calling a function.
#define FASTFLOW_RETURN_IF_ERROR(expr)                                                                                 \
    do {                                                                                                               \
        auto _status = (expr);                                                                                         \
        if ((!_status.IsOK()))                                                                                         \
            return _status;                                                                                            \
    } while (0)

// Macros for testing the results of functions that return tensorflow::Status.
#define FASTFLOW_EXPECT_OK(statement) EXPECT_EQ(::CC_NAME_SPACE_NAME::Status::OK(), (statement))
#define FASTFLOW_ASSERT_OK(statement) ASSERT_EQ(::CC_NAME_SPACE_NAME::Status::OK(), (statement))

#define FASTFLOW_MAKE_STATUS(category, code, ...)                                                                      \
    ::fastflow::Status(::fastflow::category, ::fastflow::code, ::fastflow::MakeString(__VA_ARGS__))

// macro to explicitly ignore the return value from a function call so Code Analysis doesn't complain
#define FASTFLOW_IGNORE_RETURN_VALUE(fn) \
  static_cast<void>(fn)

std::vector<std::string> GetStackTrace();

// Capture where a message is coming from. Use __FUNCTION__ rather than the much longer __PRETTY_FUNCTION__
#define FASTFLOW_WHERE ::fastflow::CodeLocation(__FILE__, __LINE__, __FUNCTION__)

#define FASTFLOW_WHERE_WITH_STACK                                                                                      \
    ::fastflow::CodeLocation(__FILE__, __LINE__, __PRETTY_FUNCTION__, ::fastflow::GetStackTrace())

// Throw an exception with optional message.
// NOTE: The arguments get streamed into a string via ostringstream::operator<<
// DO NOT use a printf format string, as that will not work as you expect.
#define FASTFLOW_THROW(...)                                                                                            \
    throw ::fastflow::FastFlowException(FASTFLOW_WHERE_WITH_STACK, ::fastflow::MakeString(__VA_ARGS__))

// Just in order to mark things as not implemented. Do not use in final code.
#define FASTFLOW_NOT_IMPLEMENTED(...) throw ::fastflow::NotImplementedException(::fastflow::MakeString(__VA_ARGS__))

// Check condition.
// NOTE: The arguments get streamed into a string via ostringstream::operator<<
// DO NOT use a printf format string, as that will not work as you expect.
#define FASTFLOW_ENFORCE(condition, ...)                                                                               \
    if (!(condition))                                                                                                  \
    throw ::fastflow::FastFlowException(FASTFLOW_WHERE_WITH_STACK, #condition, ::fastflow::MakeString(__VA_ARGS__))


#define FASTFLOW_MAKE_STATUS(category, code, ...)             \
  ::fastflow::Status(::fastflow::category, \
                                ::fastflow::code,     \
                                ::fastflow::MakeString(__VA_ARGS__))

// Check condition. if not met, return status.
#define FASTFLOW_RETURN_IF_NOT(condition, ...)                                                         \
  if (!(condition)) {                                                                                     \
    return FASTFLOW_MAKE_STATUS(FASTFLOW, FAIL, "Not satsified: " #condition "\n",                  \
                                   FASTFLOW_WHERE.ToString(), ::fastflow::MakeString(__VA_ARGS__)); \
  }

}  // namespace fastflow

#endif  // __FASTFLOW_CORE_COMMON_H___